package com.kqkj.dyrule.marketing.querier;

import com.kqkj.dyrule.marketing.beans.BufferData;
import com.kqkj.dyrule.marketing.beans.EventCombinationCondition;
import com.kqkj.dyrule.marketing.beans.EventCondition;
import com.kqkj.dyrule.marketing.buffer.BufferManagerImpl;
import com.kqkj.dyrule.marketing.utils.ConfigNames;
import com.kqkj.dyrule.marketing.utils.EventUtil;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import org.apache.flink.api.java.tuple.Tuple2;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

public class ClickHouseQuerier {
    Connection ckConn;
    BufferManagerImpl bufferManager;
    long bufferTtl;
    public ClickHouseQuerier(Connection connection){

        this.ckConn = connection;
        bufferManager = new BufferManagerImpl();
        Config load = ConfigFactory.load();

        bufferTtl = load.getLong(ConfigNames.REDIS_BUFFER_TTL);
    }

    /**
     * 根据组合条件及查询的时间范围，得到返回结果为213123形式的字符串序列
     * @param deviceId
     * @param eventCombinationCondition 行为组合条件
     * @param queryRangeStartTs 查询时间范围起始
     * @param queryRangeEndTs  查询时间范围结束
     * @return 用户做过的组合条件中的事件的字符串序列
     */
    private String queryEventCombinationConditionStr(String deviceId, EventCombinationCondition eventCombinationCondition, long queryRangeStartTs, long queryRangeEndTs) throws SQLException {
        String querySql = eventCombinationCondition.getQuerySql();
        List<EventCondition> eventConditionList = eventCombinationCondition.getEventConditionList();
        List<String> ids = eventConditionList.stream().map(e -> e.getEventId()).collect(Collectors.toList());
        PreparedStatement pst = ckConn.prepareStatement(querySql);
        pst.setString(1,deviceId);
        pst.setLong(2,queryRangeStartTs);
        pst.setLong(3,queryRangeEndTs);
        ResultSet resultSet = pst.executeQuery();
        StringBuffer sb = new StringBuffer();
        while (resultSet.next()){
            String eventId = resultSet.getString(1);
            sb.append(ids.indexOf(eventId) + 1);
        }
        return sb.toString();
    }

    public Tuple2<String,Integer> queryEventCombinationConditionCount(String deviceId, EventCombinationCondition eventCombinationCondition, long queryRangeStartTs, long queryRangeEndTs) throws SQLException {
        return queryEventCombinationConditionCount(deviceId,eventCombinationCondition,queryRangeStartTs,queryRangeEndTs,false);
    }



        /**
         * 根据组合条件及查询的时间范围，查询该组合出现的次数
         * @param deviceId
         * @param eventCombinationCondition 行为组合条件
         * @param queryRangeStartTs 查询时间范围起始
         * @param queryRangeEndTs  查询时间范围结束
         *        needWholeStr 是否需要完整的字符串
         * @return
         */
    public Tuple2<String,Integer> queryEventCombinationConditionCount(String deviceId, EventCombinationCondition eventCombinationCondition, long queryRangeStartTs, long queryRangeEndTs,boolean needWholeStr) throws SQLException {
        //缓存读处理
        /**
         * 缓存再什么情况下有用
         *  缓存数据的时间范围：[t3 -> t8]
         *  查询条件的时间范围
         *      [t3->t8]    直接用缓存结果作为方法的返回值
         *      [t3->t10]   判断缓存数据的count值是否满足>条件的阈值，成立返回结果，不成立用 缓存结果+t8->t10查询结果作为整个返回结果
         *      [t1->t8]   判断缓存数据的count值是否满足>条件的阈值，成立返回结果，不成立用 t1->t3+缓存结果查询结果作为整个返回结果
         *      [t1->t10]   判断缓存数据的count值是否满足>条件的阈值，成立返回结果，不成立  无用
         *
         *   如下逻辑可能存在
         *      valueMap中可能存在多个上述区间范围可能性
         *      下面在遍历缓存数据的时候，可能遇到一个满足条件的就直接判断并往下走了
         *   最好的实现方法
         *      先遍历一遍valueMap,从中找到最优缓存区间数据
         *      然后在判断并往下走
         */

        String bufferKey = deviceId + ":" + eventCombinationCondition.getCachedId();
        System.out.println("key："+bufferKey);
        BufferData bufferData = bufferManager.getDataFromBuffer(bufferKey);
        Map<String, String> valueMap = bufferData.getValueMap();
        Set<String> keySet = valueMap.keySet();
        System.out.println("所有的key"+keySet);
        /**
         * map key 开始时间：结束时间：插入时间
         */
        long current = System.currentTimeMillis();
        for (String key : keySet) {
            String[] split = key.split(":");
            //判断缓存是否过期，做清除动作
            long insertTime = Long.parseLong(split[2]);
            if (System.currentTimeMillis() - insertTime >= bufferTtl){
                bufferManager.delBufferEntry(bufferKey,key);
            }
            long startTime = Long.parseLong(split[0]);
            long endTime = Long.parseLong(split[1]);

            String bufferSeqStr = valueMap.get(key);
            int bufferCount = EventUtil.sequenceStrMatchRegexGroupCount(bufferSeqStr, eventCombinationCondition.getMatchPattern());
            //查询范围与缓存范围完全相同
            if(startTime == queryRangeStartTs && endTime == queryRangeEndTs){

                //将ck查询的结果写入到缓存
                bufferManager.delBufferEntry(bufferKey,key);
                HashMap<String, String> toPutMap = new HashMap<>();
                //放入之前的origin缓存数据，更新了插入时间
                toPutMap.put(startTime+":"+endTime+":"+current,bufferSeqStr);
                bufferManager.putDataToBuffer(bufferKey,toPutMap);
                return Tuple2.of(bufferSeqStr,bufferCount);
            }
            //左端点对齐，且条件的查询范围包含缓存的时间范围
            if(startTime == queryRangeStartTs && endTime <= queryRangeEndTs){
                int minLimit = eventCombinationCondition.getMinLimit();
                if (bufferCount >= minLimit && !needWholeStr){//事件次序可能导致问题(非完整时间段的str)，故须引入布尔值
                    return Tuple2.of(bufferSeqStr,bufferCount);
                } else {
                    //调整查询时间，去clickhouse中查询一小段
                    String rightStr = queryEventCombinationConditionStr(deviceId,eventCombinationCondition,endTime,queryRangeEndTs);

                    //将ck查询的结果写入到缓存
                    bufferManager.delBufferEntry(bufferKey,key);
                    HashMap<String, String> toPutMap = new HashMap<>();
                    //放入之前的origin缓存数据，更新了插入时间
                    toPutMap.put(startTime+":"+endTime+":"+current,bufferSeqStr);
                    toPutMap.put(endTime+":"+queryRangeEndTs+":"+current,rightStr);
                    toPutMap.put(startTime+":"+queryRangeEndTs+":"+current,bufferSeqStr+rightStr);
                    bufferManager.putDataToBuffer(bufferKey,toPutMap);
                    int totalCount = EventUtil.sequenceStrMatchRegexGroupCount(bufferSeqStr + rightStr, eventCombinationCondition.getMatchPattern());

                    return Tuple2.of(bufferSeqStr+rightStr,totalCount);
                }
            }
            //右端点对齐，且条件的查询范围包含缓存的时间范围
            if(startTime >= queryRangeStartTs && endTime == queryRangeEndTs){
                int minLimit = eventCombinationCondition.getMinLimit();
                if (bufferCount >= minLimit && !needWholeStr){
                    return Tuple2.of(bufferSeqStr,bufferCount);
                } else {
                    //调整查询时间，去clickhouse中查询一小段
                    String leftStr = queryEventCombinationConditionStr(deviceId,eventCombinationCondition,queryRangeStartTs,startTime);

                    //将ck查询的结果写入到缓存
                    bufferManager.delBufferEntry(bufferKey,key);
                    HashMap<String, String> toPutMap = new HashMap<>();
                    //放入之前的origin缓存数据，更新了插入时间
                    toPutMap.put(startTime+":"+endTime+":"+current,bufferSeqStr);
                    toPutMap.put(queryRangeStartTs+":"+startTime+":"+current,leftStr);
                    toPutMap.put(queryRangeStartTs+":"+endTime+":"+current,leftStr+bufferSeqStr);
                    bufferManager.putDataToBuffer(bufferKey,toPutMap);

                    int totalCount = EventUtil.sequenceStrMatchRegexGroupCount(leftStr+bufferSeqStr, eventCombinationCondition.getMatchPattern());
                    return Tuple2.of(leftStr+bufferSeqStr,totalCount);
                }
            }
            //左右端点不对齐，且条件的查询范围包含缓存的时间范围
            if(startTime >= queryRangeStartTs && endTime <= queryRangeEndTs){
                int minLimit = eventCombinationCondition.getMinLimit();
                if (bufferCount >= minLimit && !needWholeStr){
                    System.out.println("走入此方法，左右端点不对齐");
                    //将ck查询的结果写入到缓存
                    bufferManager.delBufferEntry(bufferKey,key);
                    HashMap<String, String> toPutMap = new HashMap<>();
                    //放入之前的origin缓存数据，更新了插入时间
                    toPutMap.put(startTime+":"+endTime+":"+current,bufferSeqStr);
                    bufferManager.putDataToBuffer(bufferKey,toPutMap);

                    return Tuple2.of(bufferSeqStr,bufferCount);
                }
            }
        }
        //先查询用户在组合条件中做过的事件的字符串序列
        String eventSequenceStr = queryEventCombinationConditionStr(deviceId,eventCombinationCondition,queryRangeStartTs,queryRangeEndTs);

        //将ck查询的结果写入到缓存
        HashMap<String, String> toPutMap = new HashMap<>();
        toPutMap.put(queryRangeStartTs+":"+queryRangeEndTs+":"+current,eventSequenceStr);
        bufferManager.putDataToBuffer(bufferKey,toPutMap);

        //然后取出组合条件中的正则表达式
        String matchPattern = eventCombinationCondition.getMatchPattern();
        //正则匹配得到匹配的次数
        int count = EventUtil.sequenceStrMatchRegexGroupCount(eventSequenceStr, matchPattern);

        //缓存写处理

        return Tuple2.of(eventSequenceStr,count);
    }

}
